package com.opensns.renren;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.json.JSONObject;

import android.content.Context;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;

import com.opensns.android.OauthAccessToken;
import com.opensns.android.OauthDialog;
import com.opensns.android.OauthDialog.OauthCallbackHandler;
import com.opensns.android.OpenSns;
import com.opensns.android.OpenSnsException;
import com.opensns.android.OpenSnsExceptionHandler;
import com.opensns.android.Parameters;
import com.opensns.android.Utility;

public class Renren extends OpenSns {
	
	public static final String URL_API_SERVER = "http://api.renren.com/restserver.do";
	public static final String URL_AUTHORIZE = "https://graph.renren.com/oauth/authorize";
	public static final String URL_ACCESS_TOKEN = "https://graph.renren.com/oauth/token";
	public static String APP_KEY = "";
	public static String APP_SECRET = "";
	
	// 默认为站外无 Web Server 应用的对调地址。
	public static String CALLBACK_URL = "http://graph.renren.com/oauth/login_success.html";
	
	public static String[] SCOPES = null;
	
	private static Renren mInstance;
		
	public static Renren getInstance() {
		if (mInstance == null) {
			mInstance = new Renren();
		}
		return mInstance;
	}
	
	/**
	 * 进行人人网开放平台异步授权。<br/>
	 * 具体的授权方法参考 <a href="http://wiki.dev.renren.com/wiki/Authentication">http://wiki.dev.renren.com/wiki/Authentication</a> 。
	 * @param context 调用方法的上下文。
	 * @param callback 授权回调接口。
	 */
	public void authorize(Context context, final AuthorizeCallback callback) {
		
		OauthCallbackHandler handler = new OauthCallbackHandler() {
			
			@Override
			public void onError(Exception e) {
				if (callback != null) {
					callback.onError(e);
				}
			}
			
			@Override
			public void handleResponse(String callbackUrl) {
				Parameters params = Utility.parseUrl(callbackUrl.replace("#", "?"));
				String error = params.getValue("error");
		        String errorDesc = params.getValue("error_description");
		        if (error == null && errorDesc == null) {
		        	OauthAccessToken token = new OauthAccessToken(OauthAccessToken.RENREN_TOKEN);
		        	token.accessToken = params.getValue("access_token");
		        	token.refreshToken = params.getValue("refresh_token");
		        	token.scope = params.getValue("scope");
		        	token.setExpiresIn(params.getValue("expires_in"));
		        	Renren.getInstance().setAccessToken(token);
		        	if (callback != null) {
		        		callback.onCompleted(params);
					}
		        } else if (error.equals("access_denied")) {
		        	if (callback != null) {
		        		callback.onCanceled();
					}        	
		        } else {
		        	if (callback != null) {
		        		callback.onException(new OpenSnsException(error, errorDesc));
					}
		        }
			}
			
			@Override
			public String getCallbackUrl() {
				return CALLBACK_URL;
			}
		};
		
		OauthDialog dialog = new OauthDialog(context, getOauthUrl(), handler);
		dialog.show();
	}
	
	/**
	 * 调用人人网开放平台 API。
	 * 请在 Parameters 中加入要调用的方法，版本号和 Access Token 等信息。
	 * 该方法将自动计算出签名认证，无需手动传入。<br/>
	 * 调用 API 的具体方法参考 <a href="http://wiki.dev.renren.com/wiki/API_Invocation">http://wiki.dev.renren.com/wiki/API_Invocation</a> 。<br/>
	 * 具体的 API 信息参考 <a href="http://wiki.dev.renren.com/wiki/API">http://wiki.dev.renren.com/wiki/API</a> 。
	 * @param context 调用方法的上下文。
	 * @param params 请求的参数。
	 * @return 人人网开放平台返回的响应数据。
	 * @throws OpenSnsException	平台 API 调用异常信息，由人人网开放平台返回。
	 * @throws Exception 程序异常。
	 */
	public String request(Context context, Parameters params) 
			throws OpenSnsException, Exception {
		
		String temp = getSignature(params, APP_SECRET);
		params.add("sig", temp);
		
		temp = Utility.requestPost(context, URL_API_SERVER, params, exceptionHandler);
		Log.d("Renren", temp);
		OpenSnsException ex = parseResponseException(temp);
		if (ex != null) {
			throw ex;
		}
		
		return temp;
	}
	
	/**
	 * 异步调用人人网开放平台 API。
	 * 请在 Parameters 中加入要调用的方法，版本号和 Access Token 等信息。
	 * 该方法将自动计算出签名认证，无需手动传入。<br/>
	 * 调用 API 的具体方法参考 <a href="http://wiki.dev.renren.com/wiki/API_Invocation">http://wiki.dev.renren.com/wiki/API_Invocation</a> 。<br/>
	 * 具体的 API 信息参考 <a href="http://wiki.dev.renren.com/wiki/API">http://wiki.dev.renren.com/wiki/API</a> 。
	 * @param context 调用方法的上下文。
	 * @param params 请求的参数。
	 * @param callback 异步请求的回调接口。
	 */
	public void requestAsync(
			final Context context,
			final Parameters params,
			final RequestCallback callback) {

		AsyncTask<Integer, Integer, Integer> async = new AsyncTask<Integer, Integer, Integer>() {
			
			private String response;
			private OpenSnsException mException;
			private Exception mError;

			@Override
			protected Integer doInBackground(Integer... ps) {
				try {
					response = request(context, params);
					Log.d("Renren", "Response: " + response);
					return 0;
				} catch (OpenSnsException e) {
					response = null;
					mException = e;
					return 1;
				} catch (Exception e) {
					response = null;
					mError = e;
					return 2;
				}
			}
			
			@Override
			protected void onPostExecute(Integer result) {
				super.onPostExecute(result);
				if (callback != null) {
					switch (result) {
					case 0:
						callback.onComplete(response);
						break;
					case 1:
						callback.onException(mException);
						break;
					case 2:
						callback.onError(mError);
						break;
					default:
						break;
					}
				}
			}
			
		};
		
		async.execute(0);
	}
	
	/**
	 * 从 SharePreferences 中获取人人网开放平台的 Access Token。
	 * @param context 调用方法的上下文。
	 * @return 人人网开放平台的 Access Token。
	 */
	public static OauthAccessToken restoreAccessToken(Context context) {
		return Utility.restoreAccessToken(context, OauthAccessToken.RENREN_TOKEN);
	}
	
	/**
	 * 获取人人网开放平台 Oauth2.0 的授权URL。
	 * @return 授权URL。
	 */
	private static String getOauthUrl() {
		Parameters params = new Parameters();
		params.add("client_id", APP_KEY);
		params.add("redirect_uri", CALLBACK_URL);
		params.add("response_type", "token");
		params.add("display", "mobile");
		if (SCOPES != null && SCOPES.length > 0) {
			params.add("scope", TextUtils.join(" ", SCOPES));
		}
		return Utility.buildUrl(URL_AUTHORIZE, params);
	}
	
	/**
	 * 尝试将请求响应的数据解析为异常信息。
	 * 由于人人网开放平台 API 调用错误时返回的 HTTP 状态为 200，因此需要用此方法处理异常。
	 * @param response 请求响应的数据。
	 * @return 人人网开放平台 API 调用异常信息。
	 */
	private static OpenSnsException parseResponseException(String response) {	
		try {
			OpenSnsException ex = new OpenSnsException();
			JSONObject json = new JSONObject(response);
			ex.error = json.getString("error_code");
			ex.message = json.getString("error_msg");
			ex.response = response;
			ex.status = "200";
			return ex;
		} catch (Exception e) {
			
		}
		return null;
	}
	
	
	/**
	 * 计算参数的签名认证。<br/>
	 * 具体的计算方法参考 <a href="http://dev.xiaonei.com/wiki/Calculate_signature">http://dev.xiaonei.com/wiki/Calculate_signature</a> 。
	 * @param params 调用 API 时传入的参数
	 * @param secret 应用的 APP SECRET。
	 * @return 签名认证。
	 */
	public static String getSignature(final Parameters params, String secret) {		
		int count = params.size();
		List<String> paramList = new ArrayList<String>(count);
		for (int i = 0; i < count; i++) {
			paramList.add(params.getKey(i) + "=" + params.getValue(i));
		}
		Collections.sort(paramList);
		String sign = TextUtils.join("", paramList) + secret;
		return Utility.getMessageDigest(sign, "UTF-8", "MD5");
	}
	
	private static final OpenSnsExceptionHandler exceptionHandler = new OpenSnsExceptionHandler() {
		
		@Override
		public OpenSnsException parseException(String response, int statusCode) {
			OpenSnsException ex = new OpenSnsException();
			ex.status = String.valueOf(statusCode);
			try {
				JSONObject json = new JSONObject(response);
				ex.error = json.optString("error", "");
				ex.message = json.optString("error_description", "");
			} catch (Exception e) {
				
			}
			return ex;
		}
		
	};

}
